using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace TelnetExpect
{
    /// <summary>
    /// Uses ANSI commands from base (<see cref="T:TelnetExpect.AnsiConsoleBase"/>) and calls
    /// a corresponding per-command abstract method e.g.
    /// <see cref="M:TelnetExpect.AnsiConsoleBase2.SetCursorPosition(System.Int32,System.Int32)"/>
    /// and <see cref="M:TelnetExpect.AnsiConsoleBase2.EraseInLine(TelnetExpect.AnsiConsoleBase2.ClearAction)"/>.
    /// </summary>
    public abstract class AnsiConsoleBase2 : AnsiConsoleBase
    {
        protected bool m_logText;

        protected AnsiConsoleBase2(Stream peer)
            : base(peer)
        {
        }

        #region Utilities
        protected string ShowControlChars(string str)
        {
            StringBuilder bldr = new StringBuilder(str.Length);
            foreach (char cur in str) {
                int curI = (int)cur;
                if (curI < 0x20) {
                    bldr.AppendFormat(System.Globalization.CultureInfo.InvariantCulture,
                        "\\x{0:x4}", curI);
                } else {
                    bldr.Append(cur);
                }
            }
            return bldr.ToString();
        }
        #endregion

        //----
        #region Printer
        public enum Intensity
        {
            Normal,
            Bold,
            Faint
        }

        ConsoleColor m_colorFg, m_colorBg;
        Intensity m_colorIntensity;

        public ConsoleColor CurrentColorFg
        {
            get { return m_colorFg; }
        }

        public ConsoleColor CurrentColorBg
        {
            get { return m_colorBg; }
        }


        public Intensity CurrentColorIntensity
        {
            get { return m_colorIntensity; }
        }

        protected override void OnAnsiCommand(char command, int[] vars)
        {
            if (m_logText) {
                string x = Join(vars);
                Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                    "ANSI command '{0}', vars: [{1}]", command, x));
            }
            //
            if (command == 'H') { // 04/08 -- CUP - CURSOR POSITION
                int row, column;
                const int DefaultRow = 1;
                const int DefaultColumn = 1;
                if (vars.Length >= 1) {
                    row = vars[0];
                } else {
                    row = DefaultRow;
                }
                if (vars.Length >= 2) {
                    column = vars[1];
                } else {
                    column = DefaultColumn;
                }
                row = row - 1; // Adjust for origin=(0,0) versus ANSI (1,1).
                column = column - 1;
                SetCursorPosition(row, column);
            } else if (command == 'K') { // 04/11 -- EL - ERASE IN LINE
                ClearAction action = 0;
                if (vars.Length > 0) {
                    action = (ClearAction)vars[0];
                }
                EraseInLine(action);
            } else if (command == 'm') { // 06/13 -- SGR - SELECT GRAPHIC RENDITION
                if (vars.Length == 0) {
                    vars = new int[] { 0 };
                }
                foreach (int p in vars) {
                    //TODO ((if (p == 0) { Set Default rendition))
                    if (p == 1) {
                        m_colorIntensity = Intensity.Bold;
                        UpdateColors("Bold");
                    } else if (p == 2) {
                        m_colorIntensity = Intensity.Faint;
                        UpdateColors("Faint");
                    } else if (p == 22) {
                        m_colorIntensity = Intensity.Normal;
                        UpdateColors("Normal");
                    } else if (p >= 40 && p <= 47) {
                        ConsoleColor color = AnsiColorToConsole(p, 40);
                        m_colorBg = color;
                        SetBackgroundColor(color);
                        UpdateColors("Bkgnd=" + color);
                    } else if (p >= 30 && p <= 37) {
                        ConsoleColor color = AnsiColorToConsole(p, 30);
                        m_colorFg = color;
                        SetForegroundColor(color);
                        UpdateColors("Frgnd=" + color);
                    } else {
                        Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                            "Skipping 'm' action '{0}'", p));
                    }
                }//foreach
            } else {
                string x = Join(vars);
                Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                    "Skipping ANSI command '{0}', vars: [{1}]", command, x));
            }
        }

        protected abstract void SetForegroundColor(ConsoleColor color);

        protected abstract void SetBackgroundColor(ConsoleColor color);

        //----
        protected abstract void EraseInLine(ClearAction action);

        protected abstract void SetCursorPosition(int row, int column);

        //----
        protected abstract void UpdateColors(string debugCommandName);

        private ConsoleColor AnsiColorToConsole(int ansiParameter, int colorCommandBase)
        {
            int absColor = ansiParameter - colorCommandBase;
            ConsoleColor cc;
            switch (absColor) {
                case 0:
                    cc = ConsoleColor.Black;
                    break;
                case 1:
                    cc = ConsoleColor.Red;
                    break;
                case 2:
                    cc = ConsoleColor.Green;
                    break;
                case 3:
                    cc = ConsoleColor.Yellow;
                    break;
                case 4:
                    cc = ConsoleColor.Blue;
                    break;
                case 5:
                    cc = ConsoleColor.Magenta;
                    break;
                case 6:
                    cc = ConsoleColor.Cyan;
                    break;
                case 7:
                    cc = ConsoleColor.White;
                    break;
                default:
                    throw new ArgumentException("Not a valid ANSI color number.");
            }
            return cc;
        }

        protected ConsoleColor GetColorWithIntensity(ConsoleColor cc, Intensity curIntensity)
        {
            ConsoleColor cc2;
            switch (curIntensity) {
                case Intensity.Normal:
                case Intensity.Faint:
                default:
                    if (cc <= ConsoleColor.Gray) {
                        cc2 = cc;
                    } else {
                        cc2 = cc - 8;
                    }
                    break;
                case Intensity.Bold:
                    if (cc > ConsoleColor.Gray) {
                        cc2 = cc;
                    } else {
                        cc2 = cc + 8;
                    }
                    break;
            }
            Debug.Assert(cc2 <= ConsoleColor.White, "cc2=" + cc2);
            return cc2;
        }

        private string Join(int[] vars)
        {
            const string Sepa = ",";
            StringBuilder bldr = new StringBuilder();
            Array.ForEach(vars, delegate(int cur) {
                bldr.Append(cur);
                bldr.Append(Sepa);
            });
            if (bldr.Length > Sepa.Length) {
                bldr.Length -= Sepa.Length;
            }
            return bldr.ToString();
        }

        protected enum ClearAction
        {
            ToEnd = 0,
            ToStart = 1,
            AllLine = 2
        }

#if NETCF
        /// <summary>
        /// Specifies constants that define foreground and background colors for the console.
        /// </summary>
        [Serializable]
        public enum ConsoleColor
        {
            /// <summary>
            /// The color black.
            /// </summary>
            Black = 0,
            /// <summary>
            /// The color dark blue.
            /// </summary>
            DarkBlue = 1,
            /// <summary>
            /// The color dark green.
            /// </summary>
            DarkGreen = 2,
            /// <summary>
            /// The color dark cyan (dark blue-green).
            /// </summary>
            DarkCyan = 3,
            /// <summary>
            /// The color dark red.
            /// </summary>
            DarkRed = 4,
            /// <summary>
            /// The color dark magenta (dark purplish-red).
            /// </summary>
            DarkMagenta = 5,
            /// <summary>
            /// The color dark yellow (ochre).
            /// </summary>
            DarkYellow = 6,
            /// <summary>
            /// The color gray.
            /// </summary>
            Gray = 7,
            /// <summary>
            /// The color dark gray.
            /// </summary>
            DarkGray = 8,
            /// <summary>
            /// The color blue.
            /// </summary>
            Blue = 9,
            /// <summary>
            /// The color green.
            /// </summary>
            Green = 10,
            /// <summary>
            /// The color cyan (blue-green).
            /// </summary>
            Cyan = 11,
            /// <summary>
            /// The color red.
            /// </summary>
            Red = 12,
            /// <summary>
            /// The color magenta (purplish-red).
            /// </summary>
            Magenta = 13,
            /// <summary>
            /// The color yellow.
            /// </summary>
            Yellow = 14,
            /// <summary>
            /// The color white.
            /// </summary>
            White = 15,
        }
#endif
        #endregion

    }
}
